//
//  UniquePaths.swift
//  LeetCodeSummary
//
//  Created by WangYonghe on 2020/7/13.
//  Copyright © 2020 WangYonghe. All rights reserved.
//  62. 不同路径

import UIKit

/*
 62. 不同路径
 一个机器人位于一个 m x n 网格的左上角 （起始点在下图中标记为“Start” ）。

 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角（在下图中标记为“Finish”）。

 问总共有多少条不同的路径？


 https://assets.leetcode-cn.com/aliyun-lc-upload/uploads/2018/10/22/robot_maze.png
 例如，上图是一个7 x 3 的网格。有多少可能的路径？

  

 示例 1:

 输入: m = 3, n = 2
 输出: 3
 解释:
 从左上角开始，总共有 3 条路径可以到达右下角。
 1. 向右 -> 向右 -> 向下
 2. 向右 -> 向下 -> 向右
 3. 向下 -> 向右 -> 向右
 示例 2:

 输入: m = 7, n = 3
 输出: 28
  

 提示：

 1 <= m, n <= 100
 题目数据保证答案小于等于 2 * 10 ^ 9
 */

class UniquePaths: NSObject {
    
    /*
     该题很容易想到动态规划解法
     设 到达终点坐标i,j的路径个数是 f(i,j)，根据题目，只能向右或者向下行走，
     那么到达终点之前的点一定是i-1,j坐标或者i,j-1坐标。所有路径的总和就是到达以上两个点坐标的总和f(i-1,j)和f(i,j-1)
     很容易推出状态转移公式
     f(i,j) = f(i-1,j) + f(i,j-1)
     
     我们用二维数组dp代替
     dp(i,j) = dp(i-1,j) + dp(i,j-1)
     
     */
    
    
    func uniquePaths(_ m: Int, _ n: Int) -> Int {
        
        
        var dp = [[Int]](repeating: [Int](repeating: 0, count: n+1), count: m+1)
        
        for i in 1 ... m{
            for j in 1 ... n{
                if i == 1 && j == 1{
                    dp[i][j] = 1
                }else{
                    dp[i][j] = dp[i-1][j] + dp[i][j-1]
                }
                
            }
        }
        
        return dp[m][n]
    }
    
    
    
    
    //延伸>>>>>>>>>>>>>>>>>>>>>
    
    
    /*
     63. 不同路径II
     条件同上题，现在需要考虑网格中有障碍物。
     
     网格中的障碍物和空位置分别用 1 和 0 来表示。

     说明：m 和 n 的值均不超过 100。

     示例 1:

     输入:
     [
       [0,0,0],
       [0,1,0],
       [0,0,0]
     ]
     输出: 2
     解释:
     3x3 网格的正中间有一个障碍物。
     从左上角到右下角一共有 2 条不同的路径：
     1. 向右 -> 向右 -> 向下 -> 向下
     2. 向下 -> 向下 -> 向右 -> 向右
     */
    
    func uniquePathsWithObstacles(_ obstacleGrid: [[Int]]) -> Int {
        if obstacleGrid.count == 0 || obstacleGrid[0][0] == 0 {
            return 0
        }
        
        let m = obstacleGrid.count
        let n = obstacleGrid[0].count
        
        
        var dp = [[Int]](repeating: [Int](repeating: 0, count: n), count: m)
        
        //根据是否有障碍物 初始化第一列和第一行
        for i in 0 ..< m{
            if obstacleGrid[i][0] == 0 {
                dp[i][0] = 1
            }else{
                //在第一列上 第一次遇到障碍物 后面的路径都不可能在经过了
                break
            }
        }
        for j in 0 ..< n{
            if obstacleGrid[0][j] == 0 {
                dp[0][j] = 1
            }else{
                //在第一行上 第一次遇到障碍物 后面的路径都不可能在经过了
                break
            }
        }

        for i in 1 ..< m {
            for j in 1 ..< n{
                if obstacleGrid[i][j] == 0 {
                    dp[i][j] = dp[i-1][j] + dp[i][j-1]
                }
            }
        }
        
        return dp[m-1][n-1]
    }
}
